package frontend.lexer;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Lexer {
    private final BufferedReader input;
    private String curLine;
    private Token token;
    private int wordIndex;
    private int lineNumber;
    private boolean inAnnotation;

    private final ArrayList<GramError> errorList;

    public ArrayList<GramError> getErrorList() {
        return errorList;
    }

    public Lexer(InputStream in) {
        this.input = new BufferedReader(new InputStreamReader(in));
        this.curLine = "";
        this.wordIndex = 0;
        this.lineNumber = 0;
        this.inAnnotation = false;
        this.errorList = new ArrayList<>();
    }

    /**
     * @return boolean
     * @throws IOException
     */
    public boolean next() throws IOException {
        if (curLine == null)
            return false;
        while (curLine.length() == wordIndex) {
            if ((curLine = input.readLine()) == null) {
                return false;
            }
            lineNumber++;
            wordIndex = 0;
            dealWithLine();
        }

        Token nextToken = nextToken(curLine);
        if (nextToken == null) {
            return next();
        } else {
            token = nextToken;
            return true;
        }
    }

    /**
     * 去掉本行注释部分,会被字符串"//"误导
     */
//    private void dealWithLine() {
//        StringBuilder lineWithoutAnnotation = new StringBuilder(); //删除本行注释部分
//        for (int i = 0; i < curLine.length(); i++) {//遍历当前行所有字符,查找 //
//            if (inAnnotation) {
//                //段注释取消的之后的部分
//                if (curLine.charAt(i) == '*' && i != curLine.length() - 1 && curLine.charAt(i + 1) == '/') {
//                    inAnnotation = false;
//                    i++;
//                }
//            } else {
//                //如果此行是行注释
//                if (curLine.charAt(i) == '/' && i != curLine.length() - 1 && curLine.charAt(i + 1) == '/') {
//                    break;
//                } else if (curLine.charAt(i) == '/' && i != curLine.length() - 1 && curLine.charAt(i + 1) == '*') {
//                    inAnnotation = true;
//                    i++;
//                } else {
//                    lineWithoutAnnotation.append(curLine.charAt(i));
//                }
//            }
//        }
//        curLine = lineWithoutAnnotation.toString();
//    }
    private void dealWithLine() {
        StringBuilder lineWithoutAnnotation = new StringBuilder(); //删除本行注释部分
        for (int i = 0; i < curLine.length(); i++) {//遍历当前行所有字符,查找 //
            if (inAnnotation) {
                //段注释取消的之后的部分
                if (curLine.charAt(i) == '*' && i != curLine.length() - 1 && curLine.charAt(i + 1) == '/') {
                    inAnnotation = false;
                    i++;
                }
            } else {
                // 处理字符串
                if (curLine.charAt(i) == '"') {
                    lineWithoutAnnotation.append(curLine.charAt(i));
                    i++;
                    while (i < curLine.length() && curLine.charAt(i) != '"') {
                        lineWithoutAnnotation.append(curLine.charAt(i));
                        i++;
                    }
                    if (i < curLine.length()) {
                        lineWithoutAnnotation.append(curLine.charAt(i));
                    }
                } else if (curLine.charAt(i) == '/' && i != curLine.length() - 1 && curLine.charAt(i + 1) == '/') {
                    break;
                } else if (curLine.charAt(i) == '/' && i != curLine.length() - 1 && curLine.charAt(i + 1) == '*') {
                    inAnnotation = true;
                    i++;
                } else {
                    lineWithoutAnnotation.append(curLine.charAt(i));
                }
            }
        }
        curLine = lineWithoutAnnotation.toString();
    }
    /**
     * 获取下一个保留词
     *
     * @param line
     * @return {@link Token}
     */
    private Token nextToken(String line) {
        Pattern blank = Pattern.compile("^\\s+");
        Pattern number = Pattern.compile("^(0|[1-9][0-9]*(\\.[0-9]+)?)");
        Pattern ident = Pattern.compile("^[a-zA-Z_][0-9a-zA-Z_]*");
//        Pattern string = Pattern.compile("^\"([^\"\\n]*)\""); // 添加字符串的正则表达式
        Pattern string = Pattern.compile("^\"([^\"\\\\]|\\\\.)*?\"");
        Pattern ChaCon = Pattern.compile("^'([^'\\\\]|\\\\.)*'");

        while (wordIndex < line.length()) {
            String s = line.substring(wordIndex);
            Matcher matcher = blank.matcher(s);
            if (matcher.find()) {//匹配删除空格
                wordIndex += matcher.group().length();
                continue;
            }

            Token token;
            // match keyword
            token = isKeyword(s.toLowerCase());
            if (token != null) {
                int nextIndex = wordIndex + token.getContent().length();
                if (nextIndex == line.length() || !Character.isLetterOrDigit(line.charAt(nextIndex)) && line.charAt(nextIndex) != '_') {
                    wordIndex = nextIndex;
                    return token;
                }
            }

            // match symbol
            token = isSymbol(s);
            if (token != null) {
                wordIndex += token.getContent().length();
                return token;
            }

            // match number
            matcher = number.matcher(s);
            if (matcher.find()) {
                wordIndex += matcher.group().length();
                return new Token(TKType.INTCON, line.substring(wordIndex - matcher.group().length(), wordIndex), lineNumber);
            }

            // match string
            matcher = string.matcher(s);
            if (matcher.find()) {
                wordIndex += matcher.group().length();
                return new Token(TKType.STRCON, matcher.group(), lineNumber);
            }
            // match ChaCon
            matcher = ChaCon.matcher(s);
            if (matcher.find()) {
                wordIndex += matcher.group().length();
                return new Token(TKType.CHRCON, matcher.group(), lineNumber);
            }
            // match ident
            matcher = ident.matcher(s);
            if (matcher.find()) {
                wordIndex += matcher.group().length();
                return new Token(TKType.IDENFR, line.substring(wordIndex - matcher.group().length(), wordIndex), lineNumber);
            }

            wordIndex = curLine.length();
            return new Token(TKType.TK_ERR, "Err", lineNumber);
        }
        return null;
    }

    private Token isKeyword(String line) {
        if (line.startsWith("main")) {
            return new Token(TKType.MAINTK, "main", lineNumber);
        } else if (line.startsWith("const")) {
            return new Token(TKType.CONSTTK, "const", lineNumber);
        } else if (line.startsWith("int")) {
            return new Token(TKType.INTTK, "int", lineNumber);
        } else if (line.startsWith("char")) {
            return new Token(TKType.CHARTK, "char", lineNumber);
        } else if (line.startsWith("break")) {
            return new Token(TKType.BREAKTK, "break", lineNumber);
        } else if (line.startsWith("continue")) {
            return new Token(TKType.CONTINUETK, "continue", lineNumber);
        } else if (line.startsWith("if")) {
            return new Token(TKType.IFTK, "if", lineNumber);
        } else if (line.startsWith("else")) {
            return new Token(TKType.ELSETK, "else", lineNumber);
        } else if (line.startsWith("for")) {
            return new Token(TKType.FORTK, "for", lineNumber);
        } else if (line.startsWith("getint")) {
            return new Token(TKType.GETINTTK, "getint", lineNumber);
        } else if (line.startsWith("getchar")) {
            return new Token(TKType.GETCHARTK, "getchar", lineNumber);
        } else if (line.startsWith("printf")) {
            return new Token(TKType.PRINTFTK, "printf", lineNumber);
        } else if (line.startsWith("return")) {
            return new Token(TKType.RETURNTK, "return", lineNumber);
        } else if (line.startsWith("void")) {
            return new Token(TKType.VOIDTK, "void", lineNumber);
        } else {
            return null;
        }
    }

    private Token isSymbol(String line) {
        if (line.startsWith("+")) {
            return new Token(TKType.PLUS, "+", lineNumber);
        } else if (line.startsWith("-")) {
            return new Token(TKType.MINU, "-", lineNumber);
        } else if (line.startsWith("*")) {
            return new Token(TKType.MULT, "*", lineNumber);
        } else if (line.startsWith("/")) {
            return new Token(TKType.DIV, "/", lineNumber);
        } else if (line.startsWith("%")) {
            return new Token(TKType.MOD, "%", lineNumber);
        } else if (line.startsWith("<=")) {
            return new Token(TKType.LEQ, "<=", lineNumber);
        } else if (line.startsWith(">=")) {
            return new Token(TKType.GEQ, ">=", lineNumber);
        } else if (line.startsWith("<")) {
            return new Token(TKType.LSS, "<", lineNumber);
        } else if (line.startsWith(">")) {
            return new Token(TKType.GRE, ">", lineNumber);
        } else if (line.startsWith("==")) {
            return new Token(TKType.EQL, "==", lineNumber);
        } else if (line.startsWith("!=")) {
            return new Token(TKType.NEQ, "!=", lineNumber);
        } else if (line.startsWith("=")) {
            return new Token(TKType.ASSIGN, "=", lineNumber);
        } else if (line.startsWith("&&")) {
            return new Token(TKType.AND, "&&", lineNumber);
        } else if (line.startsWith("&")) {// a类错误
            errorList.add(new GramError(lineNumber, ErrorCode.a));
            return new Token(TKType.AND, "&", lineNumber);//假装是正确的Token,方便后续语法分析
        } else if (line.startsWith("||")) {
            return new Token(TKType.OR, "||", lineNumber);
        } else if (line.startsWith("|")) {//a类错误
            errorList.add(new GramError(lineNumber, ErrorCode.a));
            return new Token(TKType.OR, "|", lineNumber);//假装是正确的Token,方便后续语法分析
        } else if (line.startsWith("(")) {
            return new Token(TKType.LPARENT, "(", lineNumber);
        } else if (line.startsWith(")")) {
            return new Token(TKType.RPARENT, ")", lineNumber);
        } else if (line.startsWith(";")) {
            return new Token(TKType.SEMICN, ";", lineNumber);
        } else if (line.startsWith(",")) {
            return new Token(TKType.COMMA, ",", lineNumber);
        } else if (line.startsWith("[")) {
            return new Token(TKType.LBRACK, "[", lineNumber);
        } else if (line.startsWith("]")) {
            return new Token(TKType.RBRACK, "]", lineNumber);
        } else if (line.startsWith("{")) {
            return new Token(TKType.LBRACE, "{", lineNumber);
        } else if (line.startsWith("}")) {
            return new Token(TKType.RBRACE, "}", lineNumber);
        } else if (line.startsWith("!")) {
            return new Token(TKType.NOT, "!", lineNumber);
        } else {
            return null;
        }
    }

    public Token getToken() {
        assert token != null;
        return token;
    }

    public int getLineNum() {
        return lineNumber;
    }
}